/*
 * soc.v
 *
 * Copyright 2024 dh33ex <dh33ex@riseup.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA or visit <http://www.gnu.org/licenses/>.
 *
 *
 */

module soc(
    input             i_clk,
    input             i_rst,

    input      [31:0] i_gpio,
    output     [31:0] o_gpio
);

    wire [31:0] pc;
    wire [31:0] instr;

    reg   [31:0] rd;
    wire  [31:0] rd_ram;
    wire  [31:0] rd_gpio;
    wire  [31:0] rd_timer;

    wire        we;
    reg         we_gpio;
    reg         we_ram;
    wire [31:0] addr;
    wire [31:0] wd;

    cpu cpu(
        .i_clk(i_clk),
        .i_rst(i_rst),
        .i_instr(instr),
        .o_PC(pc),
        .i_ram_rd(rd),
        .o_ram_we(we),
        .o_ram_addr(addr),
        .o_ram_wd(wd)
    );

    ram ram(
        .i_rst(i_rst),
        .i_clk(i_clk),
        .i_addr(addr),
        .i_wd(wd),
        .i_we(we_ram),
        .o_rd(rd_ram)
    );

    rom rom(
        .i_addr(pc),
        .o_instr(instr)
    );

    gpio gpio(
        .i_rst(i_rst),
        .i_clk(i_clk),
        .i_addr(addr),
        .i_wd(wd),
        .i_we(we_gpio),
        .i_gpio(i_gpio),
        .o_gpio(o_gpio),
        .o_rd(rd_gpio)
    );

    timer timer(
        .i_rst(i_rst),
        .i_clk(i_clk),
        .o_rd(rd_timer)
    );

    always @(addr or we or rd_gpio or rd_ram) begin : RW_MUX
        if (addr >= 32'h470 && addr < 32'h47C) begin
            we_gpio = we;
            we_ram = 1'b0;
            rd = rd_gpio;
        end else if (addr >= 32'h400 && addr < 32'h470) begin
            we_gpio = 1'b0;
            we_ram = we;
            rd = rd_ram;
        end else if (addr == 32'h47C) begin
            we_gpio = 1'b0;
            we_ram = 1'b0;
            rd = rd_timer;
        end else begin
            we_gpio = 1'b0;
            we_ram = 1'b0;
            rd = 32'hxxxxxxxx;
        end
    end

endmodule
